/*
    RoleCall provides a web service that web applications can use to find 
    the roles associated with a user that has been authenticated via OpenID. 
    Also provides a web-based user interface to manage the user roles 
    associated with partner web applications.
    
    Development of this software was supported in part by the David and Lucile 
    Packard Foundation and by the the Office Of Naval Research (ONR) 
     
    Copyright (c) 2012, 
    Monterey Bay Aquarium Research Institute - MBARI ( www.mbari.org )
    Michael Godin ( mikegodin AT users DOT sourceforge DOT net )

    RoleCall is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    RoleCall is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar (see the file COPYING.GPL).  If not, see 
    <http://www.gnu.org/licenses/>.  
*/

package org.mbari.rolecall.client.composite;

import java.util.List;

import org.mbari.rolecall.client.RoleCallServiceAsync;
import org.mbari.rolecall.client.RoleLister;
import org.mbari.rolecall.client.RoleMap;
import org.mbari.rolecall.client.widget.AddRoleDialog;
import org.mbari.rolecall.client.widget.OnFailure;
import org.mbari.rolecall.client.widget.SpacedFlowPanel;
import org.mbari.rolecall.client.widget.VerticalFlowPanel;
import org.mbari.rolecall.client.widget.WidgetUtil;
import org.mbari.rolecall.model.Relay;
import org.mbari.rolecall.model.Role;
import org.mbari.rolecall.model.User;
import org.mbari.rolecall.model.UserRole;
import org.mbari.rolecall.model.UserRoleMatch;

import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;

public class EditRelay extends Composite implements RoleLister {

    protected AddRoleDialog addRoleDialog;

    protected boolean constructed;

    protected Label editRelayTitle;

    protected Relay relay;

    protected RoleLister roleLister;

    protected RoleCallServiceAsync service;

    protected VerticalFlowPanel userPanel;

    protected VerticalFlowPanel userRoleMatchPanel;

    protected VerticalFlowPanel userRolePanel;

    protected FlowPanel vPanel;

    public EditRelay(RoleCallServiceAsync service, RoleLister roleLister) {
        super();
        this.service = service;
        this.roleLister = roleLister;
        vPanel = new VerticalFlowPanel();
        initWidget(vPanel);
    }

    protected void addUserWithRole() {
        showAddRoleDialog("", "", null, null, null);
    }

    protected void addWildCardMatch() {
        showAddRoleDialog(null, null, "", null, null);
    }

    protected void clear() {
        this.setVisible(false);
        userPanel.clear();
        userRoleMatchPanel.clear();
        userRolePanel.clear();
    }

    protected void construct() {
        editRelayTitle = new Label();
        WidgetUtil.setWeightBold(editRelayTitle);
        WidgetUtil.setFontSizeInEm(editRelayTitle, 1.5);
        vPanel.add(editRelayTitle);
        vPanel.add(new HTML(
                "<hr style='width:50%;text-align:left;margin-left:0'/>"));
        Label wildcardMatchLabel = new Label("Wildcard Matches:");
        WidgetUtil.setWeightBold(wildcardMatchLabel);
        WidgetUtil.setFontSizeInEm(wildcardMatchLabel, 1.25);
        vPanel.add(wildcardMatchLabel);
        userRoleMatchPanel = new VerticalFlowPanel();
        vPanel.add(userRoleMatchPanel);
        vPanel.add(new Button("Add Wildcard Match", new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                addWildCardMatch();
            }
        }));
        vPanel.add(new HTML(
                "<hr style='width:50%;text-align:left;margin-left:0'/>"));
        Label usersWithRolesLabel = new Label("Users with Roles:");
        WidgetUtil.setWeightBold(usersWithRolesLabel);
        WidgetUtil.setFontSizeInEm(usersWithRolesLabel, 1.25);
        vPanel.add(usersWithRolesLabel);
        userRolePanel = new VerticalFlowPanel();
        vPanel.add(userRolePanel);
        vPanel.add(new Button("Add User with Role", new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                addUserWithRole();
            }
        }));
        vPanel.add(new HTML(
                "<hr style='width:50%;text-align:left;margin-left:0'/>"));
        Label allUsersLabel = new Label("All Users:");
        WidgetUtil.setWeightBold(allUsersLabel);
        WidgetUtil.setFontSizeInEm(allUsersLabel, 1.25);
        vPanel.add(allUsersLabel);
        userPanel = new VerticalFlowPanel();
        vPanel.add(userPanel);
        vPanel.add(new HTML(
                "<hr style='width:50%;text-align:left;margin-left:0'/>"));
        vPanel.add(new Button("Clear", new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                clear();
            }
        }));
        constructed = true;
    }

    protected void deleteUserRole(UserRole userRole,
            final SpacedFlowPanel hPanel) {
        service.deleteUserRole(userRole, new AsyncCallback<Boolean>() {
            @Override
            public void onFailure(Throwable caught) {
                OnFailure.report("Could not delete role due to", caught);
            }

            @Override
            public void onSuccess(Boolean result) {
                hPanel.removeFromParent();
            }

        });
    }

    protected void deleteUserRoleMatch(UserRoleMatch userRoleMatch,
            final SpacedFlowPanel hPanel) {
        service.deleteUserRoleMatch(userRoleMatch,
                new AsyncCallback<Boolean>() {
                    @Override
                    public void onFailure(Throwable caught) {
                        OnFailure
                                .report("Could not delete role due to", caught);
                    }

                    @Override
                    public void onSuccess(Boolean result) {
                        hPanel.removeFromParent();
                    }

                });
    }

    public void edit(Relay relay) {
        if (!constructed) {
            construct();
        }
        this.relay = relay;
        clear();
        editRelayTitle.setText("Administer RoleCall properties of "
                + relay.getName());
        updateUserRoleMatches();
        updateUserRoles();
        updateAllUsers();
        this.setVisible(true);

    }

    @Override
    public void listUserRole(final UserRole userRole) {
        final SpacedFlowPanel hPanel = new SpacedFlowPanel();
        userRolePanel.add(hPanel);
        showUserInfo(hPanel, userRole.getEmail(), userRole.getOpenId());
        Role role = RoleMap.get(userRole.getRole().getId());
        hPanel.add(new InlineLabel(role.getName()));
        Anchor deleteAnchor = new Anchor("Delete!", "javascript:");
        hPanel.add(deleteAnchor);
        deleteAnchor.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                deleteUserRole(userRole, hPanel);
            }
        });
    }

    @Override
    public void listUserRoleMatch(final UserRoleMatch userRoleMatch) {
        final SpacedFlowPanel hPanel = new SpacedFlowPanel();
        userRoleMatchPanel.add(hPanel);
        showUserInfo(hPanel, userRoleMatch.getEmailPattern(), null);
        Role role = RoleMap.get(userRoleMatch.getRole().getId());
        hPanel.add(new InlineLabel(role.getName()));
        Anchor deleteAnchor = new Anchor("Delete!", "javascript:");
        hPanel.add(deleteAnchor);
        deleteAnchor.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                deleteUserRoleMatch(userRoleMatch, hPanel);
            }
        });
    }

    protected void listUserRoleMatches(List<UserRoleMatch> userRoleMatches) {
        if (null != userRoleMatches) {
            for (UserRoleMatch userRoleMatch : userRoleMatches) {
                listUserRoleMatch(userRoleMatch);
            }
        }
    }

    protected void listUserRoles(List<UserRole> userRoles) {
        if (null != userRoles) {
            for (UserRole userRole : userRoles) {
                listUserRole(userRole);
            }
        }
    }

    protected void listUsers(List<User> users) {
        for (final User user : users) {
            SpacedFlowPanel hPanel = new SpacedFlowPanel();
            userPanel.add(hPanel);
            showUserInfo(hPanel, user.getEmail(), user.getOpenId());
            if (null != user.getRoleRequest()) {
                InlineLabel rrTitle = new InlineLabel("Role Request:");
                rrTitle.getElement().getStyle().setFontWeight(FontWeight.BOLD);
                hPanel.add(rrTitle);
                InlineLabel rrLabel = new InlineLabel("Role Request:");
                rrLabel.getElement().getStyle().setColor("red");
                hPanel.add(rrLabel);
            }
            Anchor addRole = new Anchor("Add Role", "javascript:");
            hPanel.add(addRole);
            addRole.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    showAddRoleDialog(user.getEmail(), user.getOpenId(), null,
                            null, user.getRoleRequest());
                }
            });
        }
    }

    @Override
    public void removeUserRole(UserRole result) {
        // TODO Auto-generated method stub

    }

    @Override
    public void removeUserRoleMatch(UserRoleMatch result) {
        // TODO Auto-generated method stub

    }

    protected void showAddRoleDialog(String email, String openID,
            String emailMatch, String existingRoles, String roleRequest) {
        if (null == addRoleDialog) {
            addRoleDialog = new AddRoleDialog(service, this);
        }
        addRoleDialog.show(relay, email, openID, emailMatch, existingRoles,
                roleRequest);
    }

    protected void showUserInfo(SpacedFlowPanel hPanel, String email,
            String openId) {
        hPanel.add(new InlineLabel(email));
        if (null != openId) {
            hPanel.add(new InlineLabel("(" + openId + ")"));
        }
    }

    protected void updateAllUsers() {
        service.listRelayUsers(relay, new AsyncCallback<List<User>>() {
            @Override
            public void onFailure(Throwable caught) {
                OnFailure.report("Error listing users", caught);
            }

            @Override
            public void onSuccess(List<User> users) {
                listUsers(users);
            }
        });
    }

    protected void updateUserRoleMatches() {
        service.listUserRoleMatches(relay,
                new AsyncCallback<List<UserRoleMatch>>() {
                    @Override
                    public void onFailure(Throwable caught) {
                        OnFailure.report("Error listing user role matches",
                                caught);
                    }

                    @Override
                    public void onSuccess(List<UserRoleMatch> userRoleMatches) {
                        listUserRoleMatches(userRoleMatches);
                    }
                });
    }

    protected void updateUserRoles() {
        service.listUserRoles(relay, new AsyncCallback<List<UserRole>>() {
            @Override
            public void onFailure(Throwable caught) {
                OnFailure.report("Error listing user roles", caught);
            }

            @Override
            public void onSuccess(List<UserRole> userRoles) {
                listUserRoles(userRoles);
            }
        });
    }
}
